define(["./has"], function (has) {
    "use strict";
    var hasJSON = typeof JSON != "undefined";
    has.add("json-parse", hasJSON); // all the parsers work fine
    // Firefox 3.5/Gecko 1.9 fails to use replacer in stringify properly https://bugzilla.mozilla.org/show_bug.cgi?id=509184
    has.add("json-stringify", hasJSON && JSON.stringify({ a: 0 }, function (k, v) { return v || 1; }) == '{"a":1}');
    if (has("json-stringify")) {
        return JSON;
    } else {
        var escapeString = function (/*String*/str) {
            //summary:
            //		Adds escape sequences for non-visual characters, double quote and
            //		backslash and surrounds with double quotes to form a valid string
            //		literal.
            return ('"' + str.replace(/(["\\])/g, '\\$1') + '"').
                replace(/[\f]/g, "\\f").replace(/[\b]/g, "\\b").replace(/[\n]/g, "\\n").
                replace(/[\t]/g, "\\t").replace(/[\r]/g, "\\r"); // string
        };
        return {
            parse: has("json-parse") ? JSON.parse : function (str, strict) {
                // summary:
                // 		Parses a [JSON](http://json.org) string to return a JavaScript object.
                // description:
                //		This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
                // 		Throws for invalid JSON strings. This delegates to eval() if native JSON
                // 		support is not available. By default this will evaluate any valid JS expression.
                //		With the strict parameter set to true, the parser will ensure that only
                //		valid JSON strings are parsed (otherwise throwing an error). Without the strict
                // 		parameter, the content passed to this method must come
                //		from a trusted source.
                // str:
                //		a string literal of a JSON item, for instance:
                //			`'{ "foo": [ "bar", 1, { "baz": "thud" } ] }'`
                //	strict: 
                //		When set to true, this will ensure that only valid, secure JSON is ever parsed.
                // 		Make sure this is set to true for untrusted content. Note that on browsers/engines
                //		without native JSON support, setting this to true will run slower.
                if (strict && !/^([\s\[\{]*(?:"(?:\\.|[^"])+"|-?\d[\d\.]*(?:[Ee][+-]?\d+)?|null|true|false|)[\s\]\}]*(?:,|:|$))+$/.test(str)) {
                    throw new SyntaxError("Invalid characters in JSON");
                }
                return eval('(' + str + ')');
            },
            stringify: function (value, replacer, spacer) {
                //	summary:
                //		Returns a [JSON](http://json.org) serialization of an object.
                //	description:
                //		Returns a [JSON](http://json.org) serialization of an object.
                //		This function follows [native JSON API](https://developer.mozilla.org/en/JSON)
                //		Note that this doesn't check for infinite recursion, so don't do that!
                //	value:
                //		A value to be serialized. 
                //	replacer:
                //		A replacer function that is called for each value and can return a replacement
                //	spacer:
                //		A spacer string to be used for pretty printing of JSON
                //		
                //	example:
                //		simple serialization of a trivial object
                //		|	define(["dojo/json"], function(JSON){
                // 		|		var jsonStr = JSON.stringify({ howdy: "stranger!", isStrange: true });
                //		|		doh.is('{"howdy":"stranger!","isStrange":true}', jsonStr);
                var undef;
                if (typeof replacer == "string") {
                    spacer = replacer;
                    replacer = null;
                }
                function stringify(it, indent, key) {
                    if (replacer) {
                        it = replacer(key, it);
                    }
                    var val, objtype = typeof it;
                    if (objtype == "number") {
                        return isFinite(it) ? it + "" : "null";
                    }
                    if (objtype == "boolean") {
                        return it + "";
                    }
                    if (it === null) {
                        return "null";
                    }
                    if (typeof it == "string") {
                        return escapeString(it);
                    }
                    if (objtype == "function" || objtype == "undefined") {
                        return undef; // undefined
                    }
                    // short-circuit for objects that support "json" serialization
                    // if they return "self" then just pass-through...
                    if (typeof it.toJSON == "function") {
                        return stringify(it.toJSON(key), indent, key);
                    }
                    if (it instanceof Date) {
                        return '"{FullYear}-{Month+}-{Date}T{Hours}:{Minutes}:{Seconds}Z"'.replace(/\{(\w+)(\+)?\}/g, function (t, prop, plus) {
                            var num = it["getUTC" + prop]() + (plus ? 1 : 0);
                            return num < 10 ? "0" + num : num;
                        });
                    }
                    if (it.valueOf() !== it) {
                        // primitive wrapper, try again unwrapped:
                        return stringify(it.valueOf(), indent, key);
                    }
                    var nextIndent = spacer ? (indent + spacer) : "";
                    /* we used to test for DOM nodes and throw, but FF serializes them as {}, so cross-browser consistency is probably not efficiently attainable */

                    var sep = spacer ? " " : "";
                    var newLine = spacer ? "\n" : "";

                    // array
                    if (it instanceof Array) {
                        var itl = it.length, res = [];
                        for (key = 0; key < itl; key++) {
                            var obj = it[key];
                            val = stringify(obj, nextIndent, key);
                            if (typeof val != "string") {
                                val = "null";
                            }
                            res.push(newLine + nextIndent + val);
                        }
                        return "[" + res.join(",") + newLine + indent + "]";
                    }
                    // generic object code path
                    var output = [];
                    for (key in it) {
                        var keyStr;
                        if (typeof key == "number") {
                            keyStr = '"' + key + '"';
                        } else if (typeof key == "string") {
                            keyStr = escapeString(key);
                        } else {
                            // skip non-string or number keys
                            continue;
                        }
                        val = stringify(it[key], nextIndent, key);
                        if (typeof val != "string") {
                            // skip non-serializable values
                            continue;
                        }
                        // At this point, the most non-IE browsers don't get in this branch 
                        // (they have native JSON), so push is definitely the way to
                        output.push(newLine + nextIndent + keyStr + ":" + sep + val);
                    }
                    return "{" + output.join(",") + newLine + indent + "}"; // String
                }
                return stringify(value, "", "");
            }
        };
    }
});
